package com.example.weatherforecast.whtrcdn

import android.app.Application
import android.content.Context
import android.util.Log
import android.util.Xml
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.android.volley.Request
import com.android.volley.RequestQueue
import com.android.volley.toolbox.StringRequest
import com.example.weatherforecast.R
import com.example.weatherforecast.data.city.persistence.CityRequestInfo
import com.example.weatherforecast.data.city.persistence.entity.CityInfo
import com.example.weatherforecast.data.city.persistence.repository.CityInfoRepository
import com.example.weatherforecast.net.MyVolleyQueue
import com.example.weatherforecast.whtrcdn.constant.WhtrcdnConstant
import com.example.weatherforecast.whtrcdn.entity.WeatherCityInfo
import com.example.weatherforecast.whtrcdn.entity.WeatherCityInfoList
import com.example.weatherforecast.whtrcdn.entity.WeatherIndexInfo
import com.fasterxml.jackson.databind.JsonMappingException
import com.fasterxml.jackson.databind.ObjectMapper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException
import java.io.IOException
import java.lang.Exception

class WhtrcdnViewModel(application: Application) : AndroidViewModel(application) {
    private val TAG: String = WhtrcdnViewModel::class.java.simpleName
    var cityInfoRepository: CityInfoRepository ?= null

    init {

        Log.i(TAG, "view model init")

        cityInfoRepository = CityInfoRepository(getApplication())

        // 检索城市信息是否完成
        MyVolleyQueue.getMyVolleyQueueInstance(getApplication())?.requestQueue?.addRequestEventListener { request, event ->
            run {
                when (event) {
                    RequestQueue.RequestEvent.REQUEST_FINISHED -> {
                        request.url.also {
                            // Log.i(TAG, it)
                        }
                    }
                    else -> {
                        return@addRequestEventListener
                    }
                }
                when(cityLevel){
                    1, 2, 3 -> {
                        cityCurrentCount++
                        if (cityCurrentCount == cityCount){
                            cityRequestStatus.postValue(cityLevel)
                        }
                    }
                    else -> {
                        return@addRequestEventListener
                    }
                }
            }
        }
    }

    fun getAppInit(): Int {
        getApplication<Application>().getSharedPreferences(WhtrcdnConstant.SHP_FILE_NAME, Context.MODE_PRIVATE).also {
            return it.getInt(WhtrcdnConstant.KEY_SHP_APP_INIT, 0)
        }
    }

    fun setAppInit(flag: Int) {
        getApplication<Application>().getSharedPreferences(WhtrcdnConstant.SHP_FILE_NAME, Context.MODE_PRIVATE).also {
            it.edit().putInt(WhtrcdnConstant.KEY_SHP_APP_INIT, flag).apply()
        }
    }
    private var cityRequestInfo: CityRequestInfo ?= null
    val cityRequestStatus = MutableLiveData(0)
    private var cityLevel = 1
    private var cityCount = 0
    private var cityCurrentCount = 0

    // 检索一级城市代码
    fun initCityLevelOneData(){
        val cityLevelOneList = ArrayList<CityInfo>()
        cityCount = 1
        getApplication<Application>().resources.openRawResource(R.raw.city_request_data_json).also {
            cityRequestInfo = ObjectMapper().readValue(it, CityRequestInfo::class.java)
        }
        MyVolleyQueue.getMyVolleyQueueInstance(getApplication())?.addRequest(StringRequest(Request.Method.GET, cityRequestInfo?.levelOneDTO?.url, { response ->
            ObjectMapper().readValue(response, Map::class.java).entries.forEach {
                cityRequestInfo?.levelOneCityMap?.put(it.key as String, it.value as String)
                cityLevelOneList.add(
                    CityInfo(0, it.key as String, 1, "-", String((it.value as String).toByteArray(Charsets.ISO_8859_1), Charsets.UTF_8))
                )
            }

            viewModelScope.launch(Dispatchers.IO) {
                cityInfoRepository?.insertCityInfo(cityLevelOneList)
            }


        }, {
            Log.i(TAG, "initCityLevelOneData: $it")
        }))
    }

    // 检索二级城市代码
    fun initCityLevelTwoData(){
        Log.i(TAG, "initCityLevelTwoData levelOneCityMap size: " + cityRequestInfo?.levelOneCityMap?.size)
        cityCount = cityRequestInfo?.levelOneCityMap?.size!!
        cityLevel = 2

        cityRequestInfo?.levelOneCityMap?.entries?.forEach{ levelOne ->
            MyVolleyQueue.getMyVolleyQueueInstance(getApplication())?.addRequest(StringRequest(Request.Method.GET, cityRequestInfo?.levelTwoDTO?.getRequestUrl(levelOne.key), { response ->
                val cityLevelTwoList = ArrayList<CityInfo>()
                ObjectMapper().readValue(response, Map::class.java).entries.forEach {
                    cityRequestInfo?.levelTwoCityMap?.put(levelOne.key + it.key as String, it.value as String)
                    cityLevelTwoList.add(
                        CityInfo(0, levelOne.key + it.key as String, 2, levelOne.key, String((it.value as String).toByteArray(Charsets.ISO_8859_1), Charsets.UTF_8))
                    )
                }
                viewModelScope.launch(Dispatchers.IO) {
                    cityInfoRepository?.insertCityInfo(cityLevelTwoList)
                }
            }, {
                Log.i(TAG, "initCityLevelOneData: $it")
            }))
        }
    }

    // 检索三级城市代码
    fun initCityLevelThreeData(){
        cityRequestInfo?.levelTwoCityMap?.entries?.forEach{ levelTwo ->
            MyVolleyQueue.getMyVolleyQueueInstance(getApplication())?.addRequest(StringRequest(Request.Method.GET, cityRequestInfo?.levelThreeDTO?.getRequestUrl(levelTwo.key), { response ->
                val cityLevelThreeList = ArrayList<CityInfo>()
                ObjectMapper().readValue(response, Map::class.java).entries.forEach {
                    cityRequestInfo?.levelThreeCityMap?.put(levelTwo.key + it.key as String, it.value as String)
                    cityLevelThreeList.add(
                        CityInfo(0, levelTwo.key + it.key as String, 3, levelTwo.key, String((it.value as String).toByteArray(Charsets.ISO_8859_1), Charsets.UTF_8))
                    )
                }
                viewModelScope.launch(Dispatchers.IO) {
                    cityInfoRepository?.insertCityInfo(cityLevelThreeList)
                }
            }, {
                Log.i(TAG, "initCityLevelOneData: $it")
            }))
        }
    }

    @Throws(JsonMappingException::class)
    fun getCurrentWeatherCityInfoList(): ArrayList<WeatherCityInfo> {
        val cityInfoList = ArrayList<WeatherCityInfo>()
        val file = getApplication<Application>().getFileStreamPath("weather_city_info_list.json")
        file.deleteOnExit()
        file.createNewFile()
        file.inputStream().use { inputStream ->
            try {
                ObjectMapper().readValue(inputStream, WeatherCityInfoList::class.java).cityInfoList.onEach {
                    cityInfoList.add(it)
                }
            }catch (e: Exception){
                Log.i(TAG, "getCurrentWeatherCityInfoList: 数据解析异常，恢复默认城市列表数据")
                Log.i(TAG, "getCurrentWeatherCityInfoList: " + e.printStackTrace())
                cityInfoList.add(WeatherCityInfo())
                updateCurrentWeatherCityInfoList(cityInfoList)
                return cityInfoList
            }
        }
        return cityInfoList
    }

    fun updateCurrentWeatherCityInfoList(weatherCityInfoList: ArrayList<WeatherCityInfo>) {
        val file = getApplication<Application>().getFileStreamPath("weather_city_info_list.json")
        if (!file.exists()){
            file.createNewFile()
        }
        try {
            file.freeSpace
            ObjectMapper().writeValue(file, WeatherCityInfoList(weatherCityInfoList))
        }catch (e: Exception){
            Log.i(TAG, "getCurrentWeatherCityInfoList: 数据更新异常，请重启应用")
        }
    }

    fun checkCurrentWeatherCityDuplicated(weatherCityInfoList: ArrayList<WeatherCityInfo>, cityName: String): Boolean {
        weatherCityInfoList.onEach {
            if (it.name == cityName){
                return true
            }
        }
        return false
    }

    fun clearCityTableData(){
        viewModelScope.launch(Dispatchers.IO){
            cityInfoRepository?.clearCityTableData()
        }
    }

    fun queryAllCity(): List<CityInfo> {
        return cityInfoRepository?.queryAllCity()!!
    }

    // 导出城市数据
    fun exportCityDataToJsonFile(){
        viewModelScope.launch(Dispatchers.IO) {
            queryAllCity().also {
                ObjectMapper().writeValue(getApplication<Application>().getFileStreamPath("city_export_data_test.json"), it)
            }
        }
    }

    fun importCityDataFromJsonFile(){
        val cityInfo = ArrayList<CityInfo>()
        viewModelScope.launch(Dispatchers.IO) {
            try {
                ObjectMapper().readValue(
                    getApplication<Application>().resources.openRawResource(R.raw.city_export_data_json),
                    Array::class.java
                ).onEach {
                    val m = it as HashMap<*, *>
                    cityInfo.add(CityInfo((m["id"] as Int).toLong(), m["cityId"] as String, m["cityLevel"] as Int, m["parentCityId"] as String, m["cityName"] as String))
                }
                Log.i(TAG, "importCityDataFromJsonFile: " + cityInfo.size)
                cityInfoRepository?.insertCityInfo(cityInfo)
            }catch (e: Exception){
                Log.i(TAG, "importCityDataFromJsonFile: " + e.printStackTrace())
            }
        }
    }

    // 解析天气xml【指数】数据
    fun parseXmlData(): ArrayList<WeatherIndexInfo> {
        val weatherIndexInfoList = ArrayList<WeatherIndexInfo>()
        getApplication<Application>().resources.openRawResource(R.raw.whtrcdn_weather_data).also { inputStream ->
            val parser: XmlPullParser = Xml.newPullParser()
            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
            parser.setInput(inputStream, Charsets.UTF_8.toString())
            parser.nextTag()

            while (parser.nextTag() != XmlPullParser.END_TAG){
                if (parser.eventType != XmlPullParser.START_TAG) {
                    continue
                }
                if (parser.name == "zhishus"){
                    Log.i(TAG, "parseXmlData: ${parser.name}")
                    readWeatherIndexInfo(parser, weatherIndexInfoList)
                }else {
                    skip(parser)
                }
            }
        }

//        weatherIndexInfoList.forEach {
//            Log.i(TAG, "parseXmlData: $it")
//        }

        return weatherIndexInfoList
    }

    fun parseXmlData(str: String): ArrayList<WeatherIndexInfo> {
        val weatherIndexInfoList = ArrayList<WeatherIndexInfo>()
        val parser: XmlPullParser = Xml.newPullParser()
        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
        parser.setInput(str.byteInputStream(), Charsets.UTF_8.toString())
        parser.nextTag()

        while (parser.nextTag() != XmlPullParser.END_TAG){
            if (parser.eventType != XmlPullParser.START_TAG) {
                continue
            }
            if (parser.name == "zhishus"){
                Log.i(TAG, "parseXmlData: ${parser.name}")
                readWeatherIndexInfo(parser, weatherIndexInfoList)
            }else {
                skip(parser)
            }
        }

//        weatherIndexInfoList.forEach {
//            Log.i(TAG, "parseXmlData: $it")
//        }

        return weatherIndexInfoList
    }

    @Throws(XmlPullParserException::class, IOException::class)
    private fun skip(parser: XmlPullParser) {
        if (parser.eventType != XmlPullParser.START_TAG) {
            throw IllegalStateException()
        }
        var depth = 1
        while (depth != 0) {
            when (parser.next()) {
                XmlPullParser.END_TAG -> depth--
                XmlPullParser.START_TAG -> depth++
            }
        }
    }

    private fun readWeatherIndexInfo(parser: XmlPullParser, weatherIndexInfoList: ArrayList<WeatherIndexInfo>){
        parser.nextTag()
        while (parser.name != "zhishus" && parser.eventType != XmlPullParser.END_TAG){
            if (parser.name == "zhishu") {
                parser.nextTag()
                val weatherIndexInfo = WeatherIndexInfo()
                while (parser.name != "zhushi" && parser.eventType != XmlPullParser.END_TAG){
                    when(parser.name) {
                        "name" -> {
                            weatherIndexInfo.name = readIndexName(parser)
                        }
                        "value" -> {
                            weatherIndexInfo.value = readIndexValue(parser)
                        }
                        "detail" -> {
                            weatherIndexInfo.detail = readIndexDetail(parser)
                        }
                    }
                    parser.nextTag()
                }
                weatherIndexInfoList.add(weatherIndexInfo)
            }
            parser.nextTag()
        }
    }

    @Throws(IOException::class, XmlPullParserException::class)
    private fun readIndexName(parser: XmlPullParser): String {
        val ns = null
        parser.require(XmlPullParser.START_TAG, ns, "name")
        val title = readText(parser)
        parser.require(XmlPullParser.END_TAG, ns, "name")
        return title
    }

    @Throws(IOException::class, XmlPullParserException::class)
    private fun readIndexValue(parser: XmlPullParser): String {
        val ns = null
        parser.require(XmlPullParser.START_TAG, ns, "value")
        val title = readText(parser)
        parser.require(XmlPullParser.END_TAG, ns, "value")
        return title
    }

    @Throws(IOException::class, XmlPullParserException::class)
    private fun readIndexDetail(parser: XmlPullParser): String {
        val ns = null
        parser.require(XmlPullParser.START_TAG, ns, "detail")
        val title = readText(parser)
        parser.require(XmlPullParser.END_TAG, ns, "detail")
        return title
    }

    @Throws(IOException::class, XmlPullParserException::class)
    private fun readText(parser: XmlPullParser): String {
        var result = ""
        if (parser.next() == XmlPullParser.TEXT) {
            result = parser.text
            parser.nextTag()
        }
        return result
    }

}